﻿using ChatLibrary.ChatDTOs;
using Microsoft.AspNet.SignalR.Client;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using UtilitiesLib;
using Windows.Networking.Connectivity;
using Windows.UI.Popups;

namespace IGL_Talk_Win8.Utils
{
    public class ChatConnection : ObservableObject
    {
        #region Fields

        private SynchronizationContext synchronizationContext;
        private IHubProxy hub;
        private HubConnection connection;
        private static ChatConnection instance;
        private bool connected;
        private string userName;

        #endregion Fields

        #region Properties

        public static ChatConnection Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new ChatConnection();
                }
                return instance;
            }
        }

        public string UserName
        {
            get { return userName; }
            set
            {
                userName = value;
                RaisePropertyChanged("UserName");
            }
        }

        public ObservableCollection<Message> Messages { get; set; }

        public ObservableCollection<User> Users { get; set; }

        public bool NotConnected { get { return !connected; } }

        public bool Connected
        {
            get { return connected; }
            set
            {
                connected = value;
                RaisePropertyChanged("Connected");
                RaisePropertyChanged("NotConnected");
            }
        }

        #endregion Properties

        private ChatConnection()
            : base()
        {
            Messages = new ObservableCollection<Message>();
            Users = new ObservableCollection<User>();

            synchronizationContext = SynchronizationContext.Current;
            if (synchronizationContext == null)
            {
                throw new Exception("No synchronization context was specified and no default synchronization context was found.");
            }

            var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
            UserName = loader.GetString("User_NotLoggedIn");
        }

        #region Chat hub events

        private void GetMessage(Message message)
        {
            synchronizationContext.Post((arg) =>
            {
                Messages.Add(message);
            }, null);
        }

        private void UserLoggedOut(User user, List<User> users)
        {
            synchronizationContext.Post((arg) =>
            {
                FillUsers(users);
                Messages.Add(new Message() { Date = DateTime.Now, Text = UserLoggedOutMsg(user.Name), User = new User() });
            }, null);
        }

        private void UserJoined(User newUser, List<User> users)
        {
            synchronizationContext.Post((arg) =>
            {
                FillUsers(users);
                Messages.Add(new Message() { Date = DateTime.Now, Text = UserLoggedInMsg(newUser.Name), User = new User() });
            }, null);
        }

        private void FillUsers(List<User> users)
        {
            Users.Clear();
            foreach (var u in users)
            {
                Users.Add(u);
            }
            Users.Remove(Users.FirstOrDefault(u => u.Name == UserName));
        }

        #endregion Chat hub events

        public async void Connect(string login)
        {
            if(!IsInternet())
            {
                var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
                PopupConnectionError(loader.GetString("NoInternetConnection"));
                return;
            }

            connection = new HubConnection("http://wujowojtas.azurewebsites.net");
            hub = connection.CreateHubProxy("ChatHub");

            await connection.Start().ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    UserName = "ERROR";
                }
            });

            hub.On<List<User>>("registered", (users) =>
            {
                synchronizationContext.Post((arg) =>
                {
                    FillUsers(users);
                }, null);
                UserName = login;
                Connected = true;
            });

            hub.On<User, List<User>>("newUserJoined", (newUser, users) =>
            {
                UserJoined(newUser, users);
            });

            hub.On<User, List<User>>("userLoggedOut", (userName, users) =>
            {
                UserLoggedOut(userName, users);
            });

            hub.On<Message>("newMessage", (msg) =>
            {
                GetMessage(msg);
            });

            await hub.Invoke<string>("Register", login).ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    PopupConnectionError(task.Exception.GetBaseException());
                }
            });
        }

        public void Close()
        {
            synchronizationContext.Post((arg) =>
            {
                Messages.Clear();
                Users.Clear();
            }, null);

            connection.Stop();

            Connected = false;

            var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
            UserName = loader.GetString("User_NotLoggedIn");
        }

        public void SendMessage(string text)
        {
            hub.Invoke<string[]>("SendMessage", new[] { UserName, text }).ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    PopupConnectionError(task.Exception.GetBaseException());
                }
            });
        }

        private async void PopupConnectionError(Exception exception)
        {
            var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
            string messageTitle = loader.GetString("ConnectionError_Title");
            string messageText = exception.ToString();

            var messageDialog = new MessageDialog(messageText, messageTitle);

            messageDialog.Commands.Add(new UICommand(
                "Ok",
                null,
                0));

            messageDialog.DefaultCommandIndex = 0;
            messageDialog.CancelCommandIndex = 0;

            await messageDialog.ShowAsync();
        }

        private async void PopupConnectionError(string error)
        {
            var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
            string messageTitle = loader.GetString("ConnectionError_Title");
            string messageText = error;

            var messageDialog = new MessageDialog(messageText, messageTitle);

            messageDialog.Commands.Add(new UICommand(
                "Ok",
                null,
                0));

            messageDialog.DefaultCommandIndex = 0;
            messageDialog.CancelCommandIndex = 0;

            await messageDialog.ShowAsync();
        }

        private string UserLoggedInMsg(string user)
        {
            var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();

            return string.Format("{0} {1} {2}", loader.GetString("User"), user, loader.GetString("joined_chat"));
        }

        private string UserLoggedOutMsg(string user)
        {
            var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();

            return string.Format("{0} {1} {2}", loader.GetString("User"), user, loader.GetString("left"));
        }

        private static bool IsInternet()
        {
            ConnectionProfile connections = NetworkInformation.GetInternetConnectionProfile();
            bool internet = connections != null && connections.GetNetworkConnectivityLevel() == NetworkConnectivityLevel.InternetAccess;
            return internet;
        }
    }
}